package xwc.usertree;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Getter
@Setter
@Slf4j
public class Account {
    private String key; // 账户的唯一标识. key为null时表示是系统（推荐人主键）
    private String parentKey; // 上级的account key（推荐人的父主键）
    private String phone; // 账户的手机号，用来显示用
    @JSONField(serialize = false, deserialize = false)
    private Account parent; // 上级（推荐人）
    private List<Account> children = new ArrayList<>(); // 下级推荐人列表
    private BigDecimal dividendPool = BigDecimal.ZERO; // 矿池金额
    private BigDecimal depositAmount = BigDecimal.ZERO; // 本次矿池对应的入金金额
    private BigDecimal depositedTotal = BigDecimal.ZERO; // 累积入金金额
    private BigDecimal[] currentRecommenderRewardArr; // 系统结算前，推荐人奖励收益列表，系统结算后清空，重新记录直推收益
    // TODO: 额外增加一个children累积充值金额，避免太多重复计算
    private Integer newChildCountInCurrentDeposit = 0; // 账户在自身的本次入金后拉到几个新直接下级了。在本账户充值后这个数值要重置为0
    private Integer level = AccountLevels.V0; // 用户VIP级别

    public boolean isSystem() {
        return key == null;
    }

    public Account() {
    }

    public Account(String key, String phone, String parentKey, BigDecimal dividendPool, BigDecimal depositAmount,
                   BigDecimal depositedTotal, BigDecimal[] currentRecommenderRewardArr, Integer newChildCountInCurrentDeposit, Integer level) {
        this.key = key;
        this.phone = phone;
        this.parentKey = parentKey;
        this.dividendPool = dividendPool;
        this.depositAmount = depositAmount;
        this.depositedTotal = depositedTotal;
        this.currentRecommenderRewardArr = currentRecommenderRewardArr;
        this.newChildCountInCurrentDeposit = newChildCountInCurrentDeposit;
        this.level = level;
    }

    // 浅拷贝基本信息
    public Account softCopy() {
        BigDecimal[] newCurrentRecommenderRewardArr = null;
        if (currentRecommenderRewardArr != null) {
            newCurrentRecommenderRewardArr = new BigDecimal[currentRecommenderRewardArr.length];
            for (int i = 0; i < currentRecommenderRewardArr.length; i++) {
                newCurrentRecommenderRewardArr[i] = currentRecommenderRewardArr[i];
            }
        }
        return new Account(key, phone, parentKey, dividendPool, depositAmount, depositedTotal,
                newCurrentRecommenderRewardArr, newChildCountInCurrentDeposit, level);
    }

    /**
     * 添加直接推荐奖励的记录
     *
     * @param directIncome
     */
    public void addRecommenderReward(BigDecimal[] directIncome) {
        if (currentRecommenderRewardArr != null) {
            int length = currentRecommenderRewardArr.length;
            BigDecimal[] tempArr = Arrays.copyOf(currentRecommenderRewardArr, length + directIncome.length);
            for (int i = 0; i < directIncome.length; i++) {
                tempArr[length + i] = directIncome[i];
            }
            currentRecommenderRewardArr = tempArr;
        } else {
            currentRecommenderRewardArr = directIncome;
        }
    }

    /**
     * 自底向上访问account tree
     *
     * @param visitor
     */
    public void visitTreeFromDownToTop(AccountTreeVisitor visitor) {
        for (Account child : children) {
            child.visitTreeFromDownToTop(visitor);
        }
        visitor.visitAccount(this);
    }

    /**
     * 自顶向下访问account tree
     *
     * @param visitor
     */
    public void visitTreeFromTopToDown(AccountTreeVisitor visitor) {
        visitor.visitAccount(this);
        for (Account child : children) {
            child.visitTreeFromTopToDown(visitor);
        }
    }

    /**
     * 计算各子孙节点所有总的充值金额
     *
     * @return
     */
    public BigDecimal countTotalChildrenDepositedTotal() {
        BigDecimal sum = BigDecimal.ZERO;
        for (Account child : children) {
            sum = sum.add(child.getDepositedTotal());
            sum = sum.add(child.countTotalChildrenDepositedTotal());
        }
        return sum;
    }

    public Account findInTreeByKey(String key) {
        if (children == null) {
            return null;
        }
        for (Account item : children) {
            if (item == null) {
                continue;
            }
            if (item.key != null && item.key.equals(key)) {
                return item;
            }
            Account subFound = item.findInTreeByKey(key);
            if (subFound != null) {
                return subFound;
            }
        }
        return null;
    }

    @Override
    public String toString() {
        return "Account{" +
                "key='" + key + '\'' +
                ", phone='" + phone + "\'" +
                ", parentKey='" + parentKey + '\'' +
                ", dividendPool=" + dividendPool +
                ", level=" + level +
                '}';
    }

    public String toTreeString(int level) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < level; i++) {
            builder.append("\t");
        }
        builder.append(this.toString());
        if (children != null) {
            for (Account child : children) {
                builder.append("\n" + child.toTreeString(level + 1));
            }
        }
        return builder.toString();
    }

    // 以下setters在有变化前把旧值写入到snapshot中
    public void setKeyWithSnapshot(TreeSnapshot snapshot, String key) {
        snapshot.addAccountChanged(this);
        this.key = key;
    }

    public void setPhoneWithSnapshot(TreeSnapshot snapshot, String phone) {
        snapshot.addAccountChanged(this);
        this.phone = phone;
    }

    public void setParentKeyWithSnapshot(TreeSnapshot snapshot, String parentKey) {
        snapshot.addAccountChanged(this);
        this.parentKey = parentKey;
    }

    public void setParentWithSnapshot(TreeSnapshot snapshot, Account parent) {
        snapshot.addAccountChanged(this);
        this.parent = parent;
    }

    public void setChildrenWithSnapshot(TreeSnapshot snapshot, List<Account> children) {
        snapshot.addAccountChanged(this);
        this.children = children;
    }

    public void setDividendPoolWithSnapshot(TreeSnapshot snapshot, BigDecimal dividendPool) {
        log.info("userId {} {} dividend pool changed from {} to {}", this.key, this.phone, this.dividendPool.toString(), dividendPool.toString());
        snapshot.addAccountChanged(this);
        this.dividendPool = dividendPool;
    }

    public void setDepositAmountWithSnapshot(TreeSnapshot snapshot, BigDecimal depositAmount) {
        snapshot.addAccountChanged(this);
        this.depositAmount = depositAmount;
    }

    public void setDepositedTotalWithSnapshot(TreeSnapshot snapshot, BigDecimal depositedTotal) {
        log.info("user {} {} depositedTotal changed from {} to {}", this.key, this.phone, this.depositedTotal, depositedTotal);
        snapshot.addAccountChanged(this);
        this.depositedTotal = depositedTotal;
    }

    public void setNewChildCountInCurrentDepositWithSnapshot(TreeSnapshot snapshot, Integer newChildCountInCurrentDeposit) {
        snapshot.addAccountChanged(this);
        this.newChildCountInCurrentDeposit = newChildCountInCurrentDeposit;
    }

    public void setLevelWithSnapshot(TreeSnapshot snapshot, Integer level) {
        snapshot.addAccountChanged(this);
        this.level = level;
    }

    public void addChildWithSnapshot(TreeSnapshot snapshot, Account child) {
        snapshot.addAccountChanged(this);
        getChildren().add(child);
    }

    public void resetCurrentRecommenderRewardArr(TreeSnapshot snapshot) {
        snapshot.addAccountChanged(this);
        this.currentRecommenderRewardArr = null;
    }
}
